﻿using System;
using System.Collections;
using UnityEngine;


public class Climbing : MonoBehaviour
{
    [Header("References")]
    public Transform orientation;
    public PlayerMovement playerMovement;
    public Rigidbody rb;
    public Vector2 inputDirection;
    public LayerMask whatIsWall;

    public LedgeGrabbing ledgeGrabbing;
    [Header("Climbing")]
    public float climbSpeed;
    public float maxClimbTime;
    private float climbTimer;

    public bool jumpRequest;
    private bool climbing;

    [Header("ClombingJump")]
    public float climbJumpForce;
    public float climbJumpBackForce;

    public int climbJumps;
    private int climbJumpsLeft;

    [Header("Detection")]
    public float detectionLength;
    public float sphereCastRadius;
    public float maxWallLookAngle;
    public float wallLookAngle;

    private RaycastHit frontWallHit;
    public bool wallFront;
    private Transform lastWall;
    private Vector3 lastWallNormal;
    public float minWallNormalAngleChange;


    [Header("Exiting")]
    public bool exitingWall;
    public float exitWallTime;
    private float exitWallTimer;


    private void Start()
    {
        playerMovement = GetComponent<PlayerMovement>();
        rb = GetComponent<Rigidbody>();
        playerMovement.OnJumpAction += OnJumpHandler;
        ledgeGrabbing = GetComponent<LedgeGrabbing>();

    }

    private void OnJumpHandler(bool obj)
    {
        jumpRequest = obj;
    }

    private void Update()
    {
        WallCheck();
        StateMachine();
        if (climbing && !exitingWall) ClimbingMovement();
    }
    private void OnApplicationQuit()
    {
        playerMovement.OnJumpAction -= OnJumpHandler;
    }
    private void WallCheck()
    {
        wallFront = Physics.SphereCast(transform.position, sphereCastRadius, orientation.forward, out frontWallHit,detectionLength, whatIsWall);
        wallLookAngle = Vector3.Angle(orientation.forward, -frontWallHit.normal);
        bool newWall = frontWallHit.transform != lastWall || Mathf.Abs(Vector3.Angle(lastWallNormal, frontWallHit.normal)) > minWallNormalAngleChange;
        if ((newWall && wallFront)||playerMovement.isGrounded)
        {
            climbTimer = maxClimbTime;
            climbJumpsLeft = climbJumps;
        }
    }
    private void StateMachine()
    {
        inputDirection = playerMovement.inputDirection;
        if(ledgeGrabbing.holding)
        {
            if (climbing)
            {
                StopClimbing();
            }
        }
        else if(wallFront && inputDirection.y > 0f && wallLookAngle < maxWallLookAngle && !exitingWall)
        {
            if(!climbing && climbTimer > 0)
            {
                StartClimbing();
            }
            if (climbTimer > 0)
            {
                climbTimer -= Time.deltaTime;

            }
            if(climbTimer < 0)
            {
                StopClimbing();
            }

        }
        else if (exitingWall)
        {
            if (climbing)
            {
                StopClimbing();
            }
            if(exitWallTimer > 0)
            {
                exitWallTimer -= Time.deltaTime;
            }
            if(exitWallTimer < 0)
            {
                exitingWall = false;
            }

        }
        else
        {
            if (climbing)
            {
                StopClimbing();
            }
        }

        if(wallFront && jumpRequest && climbJumpsLeft > 0)
        {
            ClimbJump();
        }
    }
    private void StartClimbing()
    {
        climbing = true;
        playerMovement.climbing = true;
        lastWall = frontWallHit.transform;
        lastWallNormal = frontWallHit.normal;
    }
    private void ClimbingMovement()
    {
        rb.velocity = new Vector3(rb.velocity.x, climbSpeed, rb.velocity.z);
    }
    private void StopClimbing()
    {
        climbing = false;
        playerMovement.climbing = false;
    }
    private void ClimbJump()
    {
        if (playerMovement.isGrounded) return;
        if (ledgeGrabbing.holding || ledgeGrabbing.exitingLedge) return;
        exitingWall = true;
        exitWallTimer = exitWallTime;
        Vector3 forceToApply = transform.up * climbJumpForce + frontWallHit.normal * climbJumpBackForce;
        rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
        rb.AddForce(forceToApply, ForceMode.Impulse);
        climbJumpsLeft--;

    }
}
